//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include "psys.h"
#include "pvector.h"
#include "pmatrix.h"
#include "SeaBed.h"
#include "Fractal.h"

//==================================================================
using namespace M5D;
using namespace PMath;
using namespace PUtils;
using namespace PSYSGFX;

//==================================================================
static const float	BED_SIZE = 20000;
static const int	MAP_DIM = 256;
static const int	MAP_REPEAT = 32;
static const int	SECTION_DIM = 2;

//==================================================================
static Color4b GetColor4b( ImageBase *imagep, int x, int y )
{
	return Color4b( imagep->GetChanU8( 0, x, y ),
					imagep->GetChanU8( 1, x, y ),
					imagep->GetChanU8( 2, x, y ),
					imagep->GetChanU8( 3, x, y ) );
}

//==================================================================
static void SetColor4b( ImageBase *imagep, int x, int y, const Color4b &col )
{
	imagep->SetChanU8( 0, x, y, col._r );
	imagep->SetChanU8( 1, x, y, col._g );
	imagep->SetChanU8( 2, x, y, col._b );
	imagep->SetChanU8( 3, x, y, col._a );
}

//==================================================================
static void autotileImage( ImageBase *imagep )
{
	int	w = imagep->GetWidth();
	int	h = imagep->GetHeight();

	int	w_border = w / 4;
	int	h_border = h / 4;

	float	coe_x = 0.5f / w_border;
	for (int y=0; y < h; ++y)
	{
		for (int i=0; i < w_border; ++i)
		{
			int	x1 = w - i - 1;
			int	x2 = i;

			Color4b	col1 = GetColor4b( imagep, x1, y );
			Color4b	col2 = GetColor4b( imagep, x2, y );
		
			float	t = 0.5f - coe_x * (i + 1);
			PASSERT( t <= 0.5f && t >= 0 );

			SetColor4b( imagep, x1, y, col1.GetInterpolated( col2, t ) );
			SetColor4b( imagep, x2, y, col2.GetInterpolated( col1, t ) );
		}
	}

	float	coe_y = 0.5f / h_border;
	for (int x=0; x < w; ++x)
	{
		for (int i=0; i < h_border; ++i)
		{
			int	y1 = h - i - 1;
			int	y2 = i;

			Color4b	col1 = GetColor4b( imagep, x, y1 );
			Color4b	col2 = GetColor4b( imagep, x, y2 );

			float	t = 0.5f - coe_y * (i + 1);
			PASSERT( t <= 0.5f && t >= 0 );

			SetColor4b( imagep, x, y1, col1.GetInterpolated( col2, t ) );
			SetColor4b( imagep, x, y2, col2.GetInterpolated( col1, t ) );
		}
	}
}

//==================================================================
void SeaBed::prepareTexture()
{
	_texturep = _scenep->NewTexture( "sea texture", MAP_DIM, MAP_DIM, PUtils::ImageBase::BF_R8G8B8A8 );

	Fractal	frac( 2, 0x1111, 0.4f, 2.0f );

	ImageBase	*imgbasep = _texturep->GetImageBase();
	imgbasep->LockRW( __FILE__, __LINE__ );

		for (int i=0; i < MAP_DIM; ++i)
		{
			for (int j=0; j < MAP_DIM; ++j)
			{
				int r ,g, b;

				float	vec[2] = { j*(1.0f/MAP_DIM), i*(1.0f/MAP_DIM) };
				r = g = b = (frac.fBm( vec, 8 ) * 90) + 128;

				SetColor4b( imgbasep, j, i, Color4b( r, g, b, 255 ) );
			}
		}

		autotileImage( imgbasep );

	imgbasep->Unlock();
}

//==================================================================
SeaBed::SeaBed( RendM5D::Scene *scenep, M5D::Model *parentp ) :
	_scenep(scenep),
	_texturep(NULL),
	_materialp(NULL)
{
	_materialp = _scenep->NewMaterial( "sea mate" );	// create material
	
	_materialp->SetDiffuseCol( Vector4( 0.4f, 0.8f, 0.9f, 1.0f ) );
	
	prepareTexture();
	_materialp->SetTexture( _texturep );

	_base_modelp = _scenep->NewModel( Model::TYPE_NULL, "SeaBed Base Model", parentp );

	_meshp = _scenep->NewMesh( "sea mesh", _base_modelp );	// create a mesh
	prepareGeometry( _meshp, _materialp );
}

//==================================================================
SeaBed::~SeaBed()
{
}

//==================================================================
void SeaBed::prepareGeometry( M5D::Mesh *meshp, M5D::Material *materialp )
{
	M5D::DispList *dlistp = meshp->OpenDispListIndex( PackVert::TYPE_POSTEX, 4, 2*3 );

	// build the terrain geometry
	PackVert::PosTex	*vertsp = (PackVert::PosTex *)dlistp->BeginVerts();
		for (int i=0; i < SECTION_DIM; ++i)
			for (int j=0; j < SECTION_DIM; ++j)
			{
				vertsp[i*SECTION_DIM+j].pos = Vector3( BED_SIZE/(SECTION_DIM-1)*i, 0, BED_SIZE/(SECTION_DIM-1)*j );
				vertsp[i*SECTION_DIM+j].tex = Vector2( MAP_REPEAT*i, MAP_REPEAT*j );
			}
	dlistp->EndVerts();

//	dlistp->ResetIndexPrims();

	// add quads to the mesh
	u_short	*trigsp = dlistp->BeginAddIndexPrims( materialp, Primitive::TYPE_TRIANGLELIST, 2*3 );
	for (int i=0; i < SECTION_DIM-1; ++i)
	{
		for (int j=0; j < SECTION_DIM-1; ++j)
		{
			trigsp[0] = ((i+0) * SECTION_DIM) + (j+0);
			trigsp[1] = ((i+1) * SECTION_DIM) + (j+1);
			trigsp[2] = ((i+0) * SECTION_DIM) + (j+1);
			trigsp += 3;

			trigsp[0] = ((i+0) * SECTION_DIM) + (j+0);
			trigsp[1] = ((i+1) * SECTION_DIM) + (j+0);
			trigsp[2] = ((i+1) * SECTION_DIM) + (j+1);
			trigsp += 3;
		}
	}
	dlistp->EndAddIndexPrims();

	meshp->CloseDispListIndex();
}

//==================================================================
void SeaBed::Update()
{
	Matrix44	local_m;

	local_m = Matrix44::Tra( -BED_SIZE/2, 0, -BED_SIZE/2 );
	_base_modelp->SetMatrix( local_m );
}
